home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 1.iso / desktop / winmaze4.zip / MAZE3D.CPP < prev    next >
C/C++ Source or Header  |  1994-05-16  |  28KB  |  850 lines

  1. #include <owl\owlpch.h>
  2. #include <owl\applicat.h>
  3. #include <owl\framewin.h>
  4. #include <owl\dc.h>
  5. #include <string.h>
  6. #include <math.h>
  7. #include <stdlib.h>
  8. #include <time.h>
  9. #include "oracle.h"
  10. #include "cell.h"
  11. #include "hexmaze.h"
  12. #include "sqrmaze.h"
  13. #include "plot3d.h"
  14. #include "maze3d.rh"
  15.  
  16. #ifndef TRUE
  17. #define TRUE -1
  18. #endif
  19. #ifndef FALSE
  20. #define FALSE 0
  21. #endif
  22.  
  23. static int    hex_external_to_plot(double,double);
  24. static double hex_f(double,double);
  25. static int    hex_red(double,double);
  26. static int    sqr_external_to_plot(double,double);
  27. static double sqr_f(double,double);
  28. static int    sqr_red(double,double);
  29.  
  30. // maze constants
  31. #define PIXELS_PER_HEXAGONAL_ROOM 60               
  32. #define PIXELS_PER_SQUARE_ROOM    40                           
  33. #define RESOLUTION                 4  // larger values takes more time (and
  34.                                       // virtual memory) but produce a better 
  35.                                       // image.
  36.  
  37. hexmaze *hexmaze_ptr; // pointer to generator for maze with hexagonal rooms
  38. sqrmaze *sqrmaze_ptr; // pointer to generator for maze with square rooms
  39.  
  40. class MazeWindow : public TFrameWindow
  41.   {
  42.     public:
  43.  
  44.       MazeWindow(TWindow* parent,const char far* title);
  45.  
  46.       void CleanupWindow();
  47.  
  48.       BOOL IdleAction(long IdleCount);
  49.         // So that the program remains responsive, the plotting is done here.
  50.  
  51.       void Paint(TDC &dc,BOOL erase,TRect &dirty);
  52.         // This method is invoked when part of the display has been made
  53.         // "dirty" (for example, when another window has been moved from
  54.         // atop the display).
  55.  
  56.     protected:
  57.  
  58.       void CeActionClear(TCommandEnabler &ce);
  59.         // Determines whether the action "Clear" may be selected.
  60.  
  61.       void CeActionSolve(TCommandEnabler &ce);
  62.         // Determines whether the action "Solve" may be selected.
  63.  
  64.       void CeStyleSquare(TCommandEnabler &ce);
  65.         // Determines whether a check mark should appear to the left of the 
  66.         // style "Square rooms".
  67.  
  68.       void CeStyleHexagon(TCommandEnabler &ce);
  69.         // Determines whether a check mark should appear to the left of the
  70.         // style "Hexagonal rooms".
  71.  
  72.       void CmActionNew();
  73.         // Processes a request for a new maze.
  74.  
  75.       void CmActionSolve();
  76.         // Processes a request that a maze be solved.
  77.  
  78.       void CmActionClear();
  79.         // Processes a request that the solution be cleared from a maze.
  80.  
  81.       void CmHelpAbout();
  82.         // Processes a request for information about the author.
  83.  
  84.       void CmHelpUsage();
  85.         // Processes a request for information about using this program.
  86.  
  87.       void CmStyleSquare();
  88.         // Processes a request for square rooms.
  89.  
  90.       void CmStyleHexagon();
  91.         // Processes a request for hexagonal rooms.
  92.  
  93.       void EvHScroll(UINT code,UINT pos,HWND wnd);
  94.         // Processes a change in the horizontal scroll bar -- a request for
  95.         // a new angle of rotation.
  96.  
  97.       void EvSize(UINT sizeType,TSize &size);
  98.         // Processes resizing of the window -- a request for a different size
  99.         // maze.
  100.  
  101.       void EvVScroll(UINT code,UINT pos,HWND wnd);
  102.         // Processes a change in the vertical scroll bar -- a request for a
  103.         // new angle of tilt.
  104.  
  105.     private:
  106.   
  107.       void   DisplayMessage(const string& msg);
  108.         // Pops up a message to the user.
  109.  
  110.       double light_x;
  111.       double light_y;
  112.       double light_z;
  113.         // Vector to the major source of light.
  114.  
  115.       int    num_columns;
  116.         // Number of columns in the current maze.
  117.  
  118.       int    num_rows;
  119.         // Number of rows in the current maze.
  120.  
  121.       int    only_plot_solution;
  122.         // If TRUE, only plot the outline to the solution of the maze.
  123.  
  124.       plot3d *plot3d_ptr;
  125.         // Pointer to an instance of the 3D plotting class.
  126.  
  127.       TRect  region_to_paint;
  128.         // The part of the maze to be plotted.
  129.  
  130.       int    rotation;
  131.         // The number of degrees the maze is rotated about a line
  132.         // perpendicular to its base.
  133.  
  134.       char   seed [9];
  135.         // Random number seed (derived from the system clock) used to
  136.         // generate the maze.
  137.  
  138.       int    solve;
  139.         // TRUE if the maze is to be solved.
  140.  
  141.       int    solved;
  142.         // TRUE if the solution is currently displayed.
  143.  
  144.       char   state;
  145.         // State of the plotting; one of the following:
  146.         //     'B' -- beginning
  147.         //     'M' -- maze being generated
  148.         //     'S' -- preparing plot
  149.         //     'P' -- plotting
  150.         //     'F' -- failure
  151.         //     'D' -- done
  152.  
  153.       char   style;
  154.         // 'H' for hexagonal rooms; 'S' for square rooms.
  155.  
  156.       int    tilt;
  157.         // The number of degrees the maze is tilted towards the viewer.
  158.   
  159.       DECLARE_RESPONSE_TABLE(MazeWindow);
  160.        // Associates user commands with methods in this program.
  161.   };
  162.  
  163. DEFINE_RESPONSE_TABLE1(MazeWindow,TFrameWindow)
  164.   EV_COMMAND(CM_ACTION_NEW,CmActionNew),
  165.   EV_COMMAND(CM_ACTION_SOLVE,CmActionSolve),
  166.   EV_COMMAND(CM_ACTION_CLEAR,CmActionClear),
  167.   EV_COMMAND(CM_HELP_ABOUT,CmHelpAbout),
  168.   EV_COMMAND(CM_HELP_USAGE,CmHelpUsage),
  169.   EV_COMMAND(CM_STYLE_SQUARE,CmStyleSquare),
  170.   EV_COMMAND(CM_STYLE_HEXAGON,CmStyleHexagon),
  171.   EV_COMMAND_ENABLE(CM_ACTION_SOLVE,CeActionSolve),
  172.   EV_COMMAND_ENABLE(CM_ACTION_CLEAR,CeActionClear),
  173.   EV_COMMAND_ENABLE(CM_STYLE_SQUARE,CeStyleSquare),
  174.   EV_COMMAND_ENABLE(CM_STYLE_HEXAGON,CeStyleHexagon),
  175.   EV_WM_HSCROLL,
  176.   EV_WM_VSCROLL,
  177.   EV_WM_SIZE,
  178. END_RESPONSE_TABLE;
  179.  
  180. MazeWindow::MazeWindow(
  181.   TWindow        *parent,
  182.   const char far *title) : TFrameWindow(parent, title)
  183.     {
  184.       Attr.Style |= WS_VSCROLL | WS_HSCROLL;
  185.         // Scroll bars are used to specify tilt and rotation.
  186.       AssignMenu("MAZE_MENU");
  187.       state='B';             // beginning plot
  188.       style='S';             // square rooms
  189.       rotation=0;            // (degrees)
  190.       tilt=30;               // (degrees)
  191.       light_x=(double) 1.5;  // vector to light source
  192.       light_y=(double) -1.0;
  193.       light_z=(double) 2.6;
  194.       solve=FALSE;           // initially, don't show the solution
  195.       plot3d_ptr=new plot3d((TFrameWindow *) this); // 3D plotter
  196.     }
  197.  
  198. void MazeWindow::CleanupWindow()
  199.   {
  200.     delete plot3d_ptr; // destroy 3D plotter
  201.     TFrameWindow::CleanupWindow();
  202.   }
  203.  
  204. void MazeWindow::DisplayMessage(const string &msg)
  205. // Pops up a message to the user.
  206.   {
  207.     MessageBox(msg.c_str(),GetApplication()->GetName(),
  208.      MB_OK | MB_ICONEXCLAMATION);
  209.     return;
  210.   }
  211.  
  212. BOOL MazeWindow::IdleAction(long IdleCount)
  213. // So that the program remains responsive, the plotting is done here.
  214.   {
  215.     switch (state)
  216.       // State of the plotting; one of the following:
  217.       //     'B' -- beginning
  218.       //     'R' -- restarting
  219.       //     'M' -- maze being generated
  220.       //     'S' -- preparing plot
  221.       //     'P' -- plotting
  222.       //     'F' -- failure
  223.       //     'D' -- done
  224.       {
  225.         case 'B': // begin
  226.           {
  227.             solved=FALSE;
  228.               // The solution is not (or soon won't be) completely displayed.
  229.             GetClientRect(region_to_paint);
  230.               // The whole maze is being plotted.
  231.             SetScrollRange(SB_VERT,0,90);   
  232.               // The maze may be tilted between 0 and 90 degrees.
  233.             SetScrollRange(SB_HORZ,0,360);
  234.               // The maze may be rotated between 0 and 360 degrees.
  235.             SetScrollPos(SB_VERT,90-tilt);
  236.               // Display the current tilt on the vertical scroll bar.
  237.             SetScrollPos(SB_HORZ,rotation);
  238.               // Display the current rotation on the horizontal scroll bar.
  239.             only_plot_solution=FALSE;
  240.               // Plot the base, walls, etc.
  241.  
  242.             // Pick a maze at random. 
  243.             time_t quotient;
  244.             time_t remainder;
  245.             time_t start_time;
  246.             time(&start_time);
  247.             for (int i=0; i < 8; i++)
  248.               {
  249.                 quotient=start_time/10;
  250.                 remainder=start_time-10*quotient;
  251.                 seed[i]=char('0'+remainder);
  252.                 start_time=quotient;
  253.               }
  254.  
  255.             // Since the width of a room (in millimeters) is constant,
  256.             // the number of rows and columns in a maze is determined by
  257.             // the current size of the (client) window.
  258.             TClientDC *dc=new TClientDC(*this);
  259.             double aspect_ratio
  260.              =(double(dc->GetDeviceCaps(VERTSIZE))
  261.              /double(dc->GetDeviceCaps(HORZSIZE)))
  262.              /(double(dc->GetDeviceCaps(VERTRES))
  263.              /double(dc->GetDeviceCaps(HORZRES)));
  264.             if (style == 'S') // square rooms
  265.               {
  266.                 num_columns=(GetClientRect().Width())/PIXELS_PER_SQUARE_ROOM;
  267.                 num_rows=(int) ((2.0/sqrt(3.0))*aspect_ratio
  268.                  *double(GetClientRect().Height())
  269.                  /double(PIXELS_PER_SQUARE_ROOM));
  270.               }
  271.             else              // hexagonal rooms
  272.               {
  273.                 num_columns=(2*GetClientRect().Width())
  274.                  /(3*PIXELS_PER_HEXAGONAL_ROOM)-1;
  275.                 num_columns=2*num_columns+1;
  276.                 num_rows=(int) ((2.0/sqrt(3.0))*aspect_ratio
  277.                  *double(GetClientRect().Height())
  278.                  /double(PIXELS_PER_HEXAGONAL_ROOM));
  279.               }
  280.             delete dc;
  281.           }
  282.  
  283.           if ((num_columns >= 4) && (num_rows >= 4))
  284.             // The maze generator requires at least 4 rows and 4 columns.
  285.             {
  286.               state='M';
  287.               TClientDC *dc=new TClientDC(*this);
  288.               dc->PatBlt(this->GetClientRect(),WHITENESS);
  289.                 // Clear the (client) window.
  290.               dc->TextOut(0,0,"Generating...");
  291.                 // Let the user know the maze is being generated.
  292.               delete dc;
  293.             }
  294.           else
  295.             // If the window is too small for a maze with 4 rows or 4 columns,
  296.             // let the user know.
  297.             {
  298.               state='F';
  299.               if (! IsIconic())
  300.                 {
  301.                   TClientDC *dc=new TClientDC(*this);
  302.                   dc->PatBlt(this->GetClientRect(),WHITENESS);
  303.                   delete dc;
  304.                   DisplayMessage("You need a larger window for a maze!");
  305.                 }
  306.             }
  307.           break; 
  308.  
  309.         case 'M': // Generate maze.
  310.           {
  311.             HCURSOR oldCursor=::SetCursor(::LoadCursor(0,IDC_WAIT));
  312.             if (style == 'S')  // square rooms
  313.               sqrmaze_ptr
  314.                =new sqrmaze(num_rows,num_columns,RESOLUTION,&seed[0],this);
  315.             else               // hexagonal rooms
  316.               hexmaze_ptr
  317.                =new hexmaze(num_rows,num_columns,RESOLUTION,&seed[0],this);
  318.             ::SetCursor(oldCursor);
  319.           }
  320.           if (style == 'S') // square rooms
  321.             if (sqrmaze_ptr->constructed())
  322.               state='S';    // proceed to prepare plot
  323.             else
  324.               {
  325.                 state='F';  // failure (already announced in pop up)
  326.                 TClientDC *dc=new TClientDC(*this);
  327.                 dc->PatBlt(this->GetClientRect(),WHITENESS);
  328.                 delete dc;
  329.               }
  330.           else              // hexagonal rooms
  331.             if (hexmaze_ptr->constructed())
  332.               state='S';    // proceed to prepare plot
  333.             else
  334.               {
  335.                 state='F';  // failure (already announced in pop up)
  336.                 TClientDC *dc=new TClientDC(*this);
  337.                 dc->PatBlt(this->GetClientRect(),WHITENESS);
  338.                 delete dc;
  339.               }
  340.           break;
  341.  
  342.         case 'R': // Restart.
  343.           solved=FALSE;
  344.             // The solution is not (or soon won't be) completely displayed.
  345.           GetClientRect(region_to_paint);
  346.             // The whole maze is being plotted.
  347.           SetScrollRange(SB_VERT,0,90);   
  348.             // The maze may be tilted between 0 and 90 degrees.
  349.           SetScrollRange(SB_HORZ,0,360);
  350.             // The maze may be rotated between 0 and 360 degrees.
  351.           SetScrollPos(SB_VERT,90-tilt);
  352.             // Display the current tilt on the vertical scroll bar.
  353.           SetScrollPos(SB_HORZ,rotation);
  354.             // Display the current rotation on the horizontal scroll bar.
  355.           only_plot_solution=FALSE;
  356.             // Plot the base, walls, etc.
  357.           if (style == 'S') // square rooms
  358.             if (sqrmaze_ptr->constructed())
  359.               state='S';    // proceed to prepare plot
  360.             else
  361.               {
  362.                 state='F';  // failure (already announced in pop up)
  363.                 TClientDC *dc=new TClientDC(*this);
  364.                 dc->PatBlt(this->GetClientRect(),WHITENESS);
  365.                 delete dc;
  366.               }
  367.           else              // hexagonal rooms
  368.             if (hexmaze_ptr->constructed())
  369.               state='S';    // proceed to prepare plot
  370.             else
  371.               {
  372.                 state='F';  // failure (already announced in pop up)
  373.                 TClientDC *dc=new TClientDC(*this);
  374.                 dc->PatBlt(this->GetClientRect(),WHITENESS);
  375.                 delete dc;
  376.               }
  377.           break;
  378.  
  379.         case 'S': // prepare plot
  380.           if (style == 'S') // square rooms
  381.             switch (plot3d_ptr->prepare_plot(
  382.              sqr_f,sqrmaze_ptr->x_min(),sqrmaze_ptr->x_max(),
  383.              sqrmaze_ptr->y_min(),sqrmaze_ptr->y_max(),sqr_external_to_plot,
  384.              sqr_red,sqrmaze_ptr->num_x_divisions(),
  385.              sqrmaze_ptr->num_y_divisions(),double(rotation),double(tilt),
  386.              light_x,light_y,light_z))
  387.               {
  388.                 case 'S': // success; plot prepared
  389.                   state='P'; // proceed to plot maze
  390.                   break;
  391.                 case 'F': // failure (already announced in pop up)
  392.                   {
  393.                     state='F';
  394.                     TClientDC *dc=new TClientDC(*this);
  395.                     dc->PatBlt(this->GetClientRect(),WHITENESS);
  396.                     delete dc;
  397.                   }
  398.                   break;
  399.                 default:  // continue preparing plot
  400.                   break;
  401.               }
  402.           else              // hexagonal rooms
  403.             switch (plot3d_ptr->prepare_plot(
  404.              hex_f,hexmaze_ptr->x_min(),hexmaze_ptr->x_max(),
  405.              hexmaze_ptr->y_min(),hexmaze_ptr->y_max(),hex_external_to_plot,
  406.              hex_red,hexmaze_ptr->num_x_divisions(),
  407.              hexmaze_ptr->num_y_divisions(),double(rotation),double(tilt),
  408.              light_x,light_y,light_z))
  409.               {
  410.                 case 'S': // success; plot prepared
  411.                   state='P'; // proceed to plot maze
  412.                   break;
  413.                 case 'F': // failure (already announced in pop up)
  414.                   {
  415.                     state='F';
  416.                     TClientDC *dc=new TClientDC(*this);
  417.                     dc->PatBlt(this->GetClientRect(),WHITENESS);
  418.                     delete dc;
  419.                   }
  420.                   break;
  421.                 default:  // continue preparing plot
  422.                   break;
  423.               }
  424.           break;
  425.  
  426.         case 'P': // plotting
  427.           switch (plot3d_ptr->plot(
  428.            region_to_paint,
  429.            solve,                     // highlight solution
  430.            only_plot_solution))       // only plot solution      
  431.             {
  432.               case 'S': // success; plot complete       
  433.                 solved=solve;
  434.                 state='D'; // proceed to wait for user input
  435.                 break;
  436.               case 'F': // failure (already announced in pop up)
  437.                 solved=FALSE;
  438.                 {
  439.                   state='F';
  440.                   TClientDC *dc=new TClientDC(*this);
  441.                   delete dc;
  442.                 }
  443.                 break;
  444.               default:  // continue
  445.                 break;
  446.             }
  447.           break;
  448.  
  449.         case 'F': // failed; wait for user input
  450.           break;
  451.  
  452.         default:  // done; wait for user input
  453.           break;
  454.       }
  455.     TFrameWindow::IdleAction(IdleCount);
  456.     return TRUE;
  457.   }
  458.  
  459. void MazeWindow::Paint(TDC &dc,BOOL erase,TRect &dirty)
  460. // This method is invoked when part of the display has been made "dirty"
  461. // (for example, when another window has been moved from atop the display).
  462.   {
  463.      only_plot_solution=FALSE;
  464.       // the walls, etc. around the solution are probably dirty too
  465.      switch (state)
  466.        {
  467.          case 'B': // beginning
  468.              // Nothing is displayed yet; nothing need be restored.
  469.            break;
  470.          case 'M': // generating maze
  471.            dc.PatBlt(this->GetClientRect(),WHITENESS);
  472.            dc.TextOut(0,0,"Generating...");
  473.              // Redisplay the message.
  474.            break;
  475.          case 'S': // preparing plot
  476.            dc.PatBlt(this->GetClientRect(),WHITENESS);
  477.            dc.TextOut(0,0,"Preparing plot...");
  478.              // The internal state of the 3D plotter is not available here,
  479.              // so display this message.
  480.            break;
  481.          case 'P': // still plotting
  482.            GetClientRect(region_to_paint);
  483.            state='P';
  484.            plot3d_ptr->restart_plot();
  485.              // Proceed to restore the entire display.  Because the painter's
  486.              // algorithm is used, areas outside the "dirty" region may still
  487.              // need plotting.
  488.            break;
  489.          case 'F': // failed
  490.              // Nothing is being displayed; nothing need be restored.
  491.            break;
  492.          default:  // done
  493.            state='P';
  494.            region_to_paint=dirty;
  495.            plot3d_ptr->restart_plot();
  496.              // Redo the "dirty" region.
  497.            break;
  498.        }
  499.   }
  500.  
  501. void MazeWindow::EvHScroll(UINT code,UINT pos,HWND wnd)
  502. // Processes a change in the horizontal scroll bar -- a request for a new angle
  503. // of rotation.
  504.   {
  505.     TFrameWindow::EvHScroll(code,pos,wnd);
  506.     int change=FALSE;
  507.     switch (code) 
  508.       {
  509.         case SB_LINELEFT:
  510.           rotation--;
  511.           if (rotation < 0)
  512.             rotation=0;
  513.           change=TRUE;
  514.           break;
  515.         case SB_LINERIGHT:
  516.           rotation++;
  517.           if (rotation > 360)
  518.             rotation=360;
  519.           change=TRUE;
  520.           break;
  521.         case SB_PAGELEFT:
  522.           rotation-=10;
  523.           if (rotation < 0)
  524.             rotation=0;
  525.           change=TRUE;
  526.           break;
  527.         case SB_PAGERIGHT:
  528.           rotation+=10;
  529.           if (rotation > 360)
  530.             rotation=360;
  531.           change=TRUE;
  532.           break;
  533.         case SB_THUMBPOSITION:
  534.           rotation=pos;
  535.           change=TRUE;
  536.           break;
  537.         case SB_THUMBTRACK:
  538.           break;
  539.         default:
  540.           break;
  541.       }
  542.     if (change)
  543.       {
  544.         delete plot3d_ptr;
  545.         state='R'; // proceed to completely redo plot
  546.         plot3d_ptr=new plot3d((TFrameWindow *) this);
  547.       }
  548.     return;
  549.   }
  550.  
  551. void MazeWindow::EvSize(UINT sizeType,TSize &size)
  552. // Processes resizing of the window -- a request for a different size maze.
  553.   {
  554.     // get rid of old maze
  555.     if (style == 'S') // square rooms
  556.       {
  557.         delete sqrmaze_ptr;
  558.         sqrmaze_ptr=NULL;
  559.       }
  560.     else              // hexagonal rooms
  561.       {
  562.         delete hexmaze_ptr;
  563.         hexmaze_ptr=NULL;
  564.       }
  565.     delete plot3d_ptr; // get rid of old 3D plotter
  566.     solve=FALSE;       // initially, don't display the solution 
  567.     state='B';         // proceed to do new maze
  568.     plot3d_ptr=new plot3d((TFrameWindow *) this);
  569.     return;
  570.   }
  571.  
  572. void MazeWindow::EvVScroll(UINT code,UINT pos,HWND wnd)
  573. // Processes a change in the vertical scroll bar -- a request for a new angle of
  574. // tilt.
  575.   {
  576.     TFrameWindow::EvVScroll(code,pos,wnd);
  577.     int change=FALSE;
  578.     switch (code) 
  579.       {
  580.         case SB_LINEUP:
  581.           tilt++;
  582.           if (tilt > 90)
  583.             tilt=90;
  584.           change=TRUE;
  585.           break;
  586.         case SB_LINEDOWN:
  587.           tilt--;
  588.           if (tilt < 0)
  589.             tilt=0;
  590.           change=TRUE;
  591.           break;
  592.         case SB_PAGEUP:
  593.           tilt+=5;
  594.           if (tilt > 90)
  595.             tilt=90;
  596.           change=TRUE;
  597.           break;
  598.         case SB_PAGEDOWN:
  599.           tilt-=5;
  600.           if (tilt < 0)
  601.             tilt=0;
  602.           change=TRUE;
  603.           break;
  604.         case SB_THUMBPOSITION:
  605.           tilt=90-pos;
  606.           change=TRUE;
  607.           break;
  608.         case SB_THUMBTRACK:
  609.           break;
  610.         default:
  611.           break;
  612.       }
  613.     if (change)
  614.       {
  615.         delete plot3d_ptr;
  616.         state='R'; // proceed to completely redo plot
  617.         plot3d_ptr=new plot3d((TFrameWindow *) this);
  618.       }
  619.     return;
  620.   }
  621.  
  622. void MazeWindow::CmActionNew()
  623. // Processes a request for a new maze.
  624.   {
  625.     // get rid of old maze
  626.     if (style == 'S') // square rooms
  627.       {
  628.         delete sqrmaze_ptr;
  629.         sqrmaze_ptr=NULL;
  630.       }
  631.     else              // hexagonal rooms
  632.       {
  633.         delete hexmaze_ptr;
  634.         hexmaze_ptr=NULL;
  635.       }
  636.     delete plot3d_ptr; // get rid of old 3D plotter
  637.     solve=FALSE;       // initially, don't display the solution 
  638.     state='B';         // proceed to do new maze
  639.     plot3d_ptr=new plot3d((TFrameWindow *) this);
  640.     return;
  641.   }
  642.  
  643. void MazeWindow::CeActionSolve(TCommandEnabler& ce)
  644. // Determines whether the action "Solve" may be selected.
  645.   {
  646.     ce.Enable((state == 'D') && (! solved));
  647.     // "Solve" may be selected if the plotting is done and the maze has
  648.     // not yet been solved.
  649.   }
  650.  
  651. void MazeWindow::CmActionSolve()
  652. // Processes a request that a maze be solved.
  653.   {
  654.     if (state == 'D') // plotting done
  655.       {
  656.         if (! solved) // not solved yet
  657.           {
  658.             only_plot_solution=TRUE;        // only the solution need be plotted
  659.             solve=TRUE;
  660.             plot3d_ptr->restart_plot();     // set the 3D plotter to restart
  661.             GetClientRect(region_to_paint); // whole solution will be plotted
  662.             state='P';                      // proceed to plot the solution
  663.           }
  664.       }
  665.     return;
  666.   }
  667.  
  668. void MazeWindow::CeActionClear(TCommandEnabler& ce)
  669. // Determines whether the action "Clear" may be selected.
  670.   {
  671.     ce.Enable((state == 'D') && (solved));
  672.     // "Clear" may be selected if the plotting is done and the solution is
  673.     // currently displayed.
  674.   }
  675.  
  676. void MazeWindow::CmActionClear()
  677. // Processes a request that the solution be cleared from a maze.
  678.   {
  679.     if (state == 'D') // plotting done
  680.       {
  681.         if (solved)   // solution displayed
  682.           {
  683.             only_plot_solution=TRUE;        // only the solution need be cleared
  684.             solve=FALSE;
  685.             plot3d_ptr->restart_plot();     // set the 3D plotter to restart
  686.             GetClientRect(region_to_paint); // whole solution will be cleared
  687.             state='P';                      // proceed to clear the solution
  688.           }
  689.       }
  690.     return;
  691.   }
  692.  
  693. void MazeWindow::CmHelpAbout()
  694. // Processes a request for information about the author.
  695.   {
  696.     MessageBox("\tMAZE3D\n\n"
  697.      "Written on May 2, 1994 by\n"
  698.      "\tJames L. Dean\n"
  699.      "\t406 40th Street\n"
  700.      "\tNew Orleans, LA 70124-1532\n"
  701.      "\tcsvcjld@nomvs.lsumc.edu\n",
  702.      GetApplication()->GetName(),MB_ICONINFORMATION);
  703.     return;
  704.   }
  705.  
  706. void MazeWindow::CmHelpUsage()
  707. // Processes a request for information about using this program.
  708.   {
  709.     MessageBox("\t\tMAZE3D\n\n"
  710.      "     The following options are available under \"Action\":\n"
  711.      "\tNew -- generate another maze\n"
  712.      "\tSolve -- outline the solution\n"
  713.      "\tClear -- remove the solution\n\n"
  714.      "     Under \"Style\" you may select square or\n"
  715.      "hexagonal rooms.\n\n"
  716.      "     Use the horizontal scroll bar to vary the\n"
  717.      "rotation form 0 to 360 degrees.\n\n"
  718.      "     Use the vertical scroll bar to vary the tilt from\n"
  719.      "0 to 90 degrees.\n\n"
  720.      "     Resizing the window yields a new maze.  The size\n"
  721.      "of the rooms remains the same.",
  722.      GetApplication()->GetName(),MB_ICONINFORMATION);
  723.     return;
  724.   }
  725.  
  726. void MazeWindow::CeStyleSquare(TCommandEnabler& ce)
  727. // Determines whether a check mark should appear to the left of the style
  728. // "Square rooms".
  729.   {
  730.     ce.SetCheck(style == 'S');
  731.   }
  732.  
  733. void MazeWindow::CmStyleSquare()
  734. // Processes a request for square rooms.
  735.   {
  736.     if (style != 'S') // style is currently hexagonal rooms
  737.       {
  738.         delete hexmaze_ptr; // get rid of maze generator for hexagonal rooms
  739.         hexmaze_ptr=NULL;   
  740.         delete plot3d_ptr;  // get rid of the current 3D plotter
  741.         style='S';          // style is now square rooms
  742.         solve=FALSE;        // initially, don't display the solution
  743.         state='B';          // proceed to do new maze
  744.         plot3d_ptr=new plot3d((TFrameWindow *) this);
  745.       }
  746.     return;
  747.   }
  748.  
  749. void MazeWindow::CeStyleHexagon(TCommandEnabler& ce)
  750. // Determines whether a check mark should appear to the left of the style
  751. // "Hexagonal rooms".
  752.   {
  753.     ce.SetCheck(style == 'H');
  754.   }
  755.  
  756. void MazeWindow::CmStyleHexagon()
  757.   {
  758. // Processes a request for hexagonal rooms.
  759.     if (style != 'H') // style is currently square rooms
  760.       {
  761.         delete sqrmaze_ptr; // get rid of maze generator for square rooms
  762.         sqrmaze_ptr=NULL;
  763.         delete plot3d_ptr;  // get rid of current 3D plotter
  764.         style='H';          // style is now square rooms
  765.         solve=FALSE;        // initially, don't display the solution
  766.         state='B';          // proceed to do new maze
  767.         plot3d_ptr=new plot3d((TFrameWindow *) this);
  768.       }
  769.     return;
  770.   }
  771.  
  772. static int hex_external_to_plot(
  773.   double x,
  774.   double y)
  775. // Returns TRUE if and only if a point is external to the maze with hexagonal
  776. // rooms.
  777.     {
  778.        return hexmaze_ptr->external_to_maze(x,y);
  779.     }
  780.  
  781. static int hex_red(
  782.   double x,
  783.   double y)
  784. // Returns TRUE if and only if a point lies on top of a wall adjacent to the
  785. // solution of the maze with hexagonal rooms.
  786.     {
  787.        return hexmaze_ptr->part_of_solution(x,y);
  788.     }
  789.  
  790. static double hex_f(
  791.   double x,
  792.   double y)
  793. // Returns a positive value for a point on top of a wall of the maze with
  794. // hexagonal rooms, 0 otherwise.
  795.     {
  796.        return hexmaze_ptr->f(x,y);
  797.     }
  798.  
  799. static int sqr_external_to_plot(
  800.   double x,
  801.   double y)
  802. // Returns TRUE if and only if a point is external to the maze with square
  803. // rooms.
  804.     {
  805.        return sqrmaze_ptr->external_to_maze(x,y);
  806.     }         
  807.  
  808. static int sqr_red(
  809.   double x,
  810.   double y)
  811. // Returns TRUE if and only if a point lies on top of a wall adjacent to the
  812. // solution of the maze with square rooms.
  813.     {
  814.        return sqrmaze_ptr->part_of_solution(x,y);
  815.     }
  816.  
  817. static double sqr_f(
  818.   double x,
  819.   double y)
  820. // Returns a positive value for a point on top of a wall of the maze with
  821. // square rooms, 0 otherwise.
  822.     {
  823.        return sqrmaze_ptr->f(x,y);
  824.     }
  825.  
  826. class MazeApp : public TApplication
  827.   {
  828.     public:
  829.            MazeApp() : TApplication("3D Mazes") {}
  830.       void InitMainWindow();
  831.   };
  832.  
  833. void MazeApp::InitMainWindow()
  834.   {
  835.     MainWindow=new MazeWindow(0,"3D Mazes");
  836.     MainWindow->SetIcon(this,"MAZE_ICON");
  837.   }
  838.  
  839. int OwlMain(int /*argc*/, char* /*argv*/ [])
  840. // Execution of this program starts here.
  841.   {
  842.     hexmaze_ptr=NULL;       // Initialize pointers to the maze generators, one
  843.     sqrmaze_ptr=NULL;       // for hexagonal rooms and one for square rooms.
  844.     int rc=MazeApp().Run(); // Process Windows messages until user is done.
  845.     delete sqrmaze_ptr;     // Get rid of any maze generators.
  846.     delete hexmaze_ptr;
  847.     return rc;
  848.   }
  849.  
  850.